/* call sendto or drop the packet.  Packets are only dropped if
 * DROP_COUNT > 0, in which case (approximately or exactly) one out
 * of every DROP_COUNT packets is dropped.
 *
 * if PREDICTABLE_DROP == 1, one packet is dropped after every
 *   DROP_COUNT - 1 packets are sent, so the drop fraction is 1/DROP_COUNT
 * if PREDICTABLE_DROP == 0, a random number generator is used to drop
 *   approximately 1/DROP_COUNT packets.  In this case, the random
 *   number sequence will be the same every time the program is run,
 *   unless IRREPRODUCIBLE_SEQUENCE is defined.
 *
 * if #DEBUG is defined, packet drops are printed.
 */

/* Copyright (C) 2007, esb@hawaii.edu.  This software is released under
 * the GNU General Public Licence, version 3 or later, normally available
 * from http://www.gnu.org/licenses/    This program is distributed 
 * WITHOUT ANY WARRANTY whatsoever -- see the license for more details.
 */

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>

#define	DROP_COUNT 10		/* change this to drop packets */
#define	PREDICTABLE_DROP 1	/* this specifies predictable or random drop */
#define DEBUG

/*
#define	IRREPRODUCIBLE_SEQUENCE // when defined, every run has different drops
*/
/*
                  // when defined, prints when dropping a packet
 */
#ifdef IRREPRODUCIBLE_SEQUENCE
#include <sys/time.h>
#include <time.h>
#endif /* IRREPRODUCIBLE_SEQUENCE */

ssize_t error_send (int s, const void *buf, size_t len, int flags,
		    const struct sockaddr *to, socklen_t tolen) {
  static int count_since_last_drop = 0;
  int drop_packet;
#ifdef IRREPRODUCIBLE_SEQUENCE
  static int initialized = 0;

  if (! initialized) {
    unsigned short seedv [3];
    struct timeval now;

    gettimeofday (&now, NULL);
    seedv [0] = now.tv_usec / 0x10000;
    seedv [1] = now.tv_sec % 0x10000;
    seedv [2] = now.tv_sec / 0x10000;
    seed48 (seedv);		/* initialize random number generator */
    initialized = 1;
  }
#endif /* IRREPRODUCIBLE_SEQUENCE */

  drop_packet = 0;
  if (DROP_COUNT != 0) {
    ++count_since_last_drop;
    if (PREDICTABLE_DROP) {
      drop_packet = (count_since_last_drop >= DROP_COUNT);
    } else {			/* random drop */
      drop_packet = (drand48 () < (1.0 / ((double) DROP_COUNT)));
    }
    if (drop_packet) {
#ifdef DEBUG
      printf ("\ndropping packet, count reached %d\n", count_since_last_drop);
#endif /* DEBUG */
      count_since_last_drop = 0;
      errno = EAGAIN;		/* resource temporarily unavailable */
      return -1;
    } /* else send packet */
  }
  return sendto(s, buf, len, flags, to, tolen);
}

/*
#define TEST_CODE              // only use to test
*/

#ifdef TEST_CODE
int main (int argc, char ** argv) {
  int i, loops, dropcount;
  char buffer [100];

  if ((argc != 2) || (atoi (argv [1]) <= 0)) {
    printf ("usage: %s loopcount\n", argv [0]);
    exit (1);
  }
  loops = atoi (argv [1]);
  dropcount = 0;
  for (i = 0; i < loops; i++) {
    errno = 0;
    error_send (0 /* no such socket! */, buffer, 10, 0, NULL, 0);
    if (errno == EAGAIN) {
      dropcount++;
    }
  }
  printf ("%d packets dropped out of %d calls\n", dropcount, loops);
}
#endif /* TEST_CODE */